﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Data.Common;
using DataAccessor.Exceptions;
using System.ComponentModel;

namespace DataAccessor
{
    /// <summary>
    /// 数据访问接口
    /// </summary>
    public abstract partial class IDataAccessor : IDisposable
    {
        #region 构造函数
        /// <summary>
        /// 从配置文件中创建DataAccessor的一个实例。
        /// </summary>
        /// <param name="databaseName">配置文件中数据库节点的名称。</param>
        /// <param name="configName">配置名</param>
        public IDataAccessor(string databaseName, string configName)
        {
            ConnectionString = HolisticCache.Holistic.Database.Single(database => database.Name.Equals(databaseName, StringComparison.CurrentCultureIgnoreCase)).ConnectionString;
            CreateConnection();

            StatementArray = TSQLCache.GetStatementCollection(databaseName, configName);
        }

        /// <summary>
        /// 从指定的连接字符串创建DataAccessor的一个实例。
        /// </summary>
        /// <param name="connectionString">连接字符串</param>
        public IDataAccessor(string connectionString)
        {
            this.ConnectionString = connectionString;
            CreateConnection();
        }
        #endregion

        #region 常量
        /// <summary>
        /// 传入的参数数量不等于需要的参数数量
        /// </summary>
        protected const string PARAM_QUANTITY_NOT_EQUAL_TEXT = "传入的参数个数不等于需要的参数个数。";
        /// <summary>
        /// 为不需要参数的语句提供了参数
        /// </summary>
        protected const string DID_NOT_NEED_PARAM_TEXT = "为不需要参数的语句提供了参数。";
        /// <summary>
        /// 缺少参数
        /// </summary>
        protected const string MISSING_PARAM_VALUE_FORMAT = "缺少参数{0}。";
        /// <summary>
        /// 无法从把{0}转换为{1}类型
        /// </summary>
        protected const string ENUM_CONVERT_ERROR_FORMAT = "无法从把{0}转换为{1}类型。";
        /// <summary>
        /// 没有创建连接。
        /// </summary>
        protected const string NULL_CONNECTION_TEXT = "没有创建连接。";
        /// <summary>
        /// 事务为空，请先调用BeginTransaction()方法创建事务。
        /// </summary>
        protected const string TRANSACTIONNULLEXCEPTION_MESSAGE = "事务为空，请先调用BeginTransaction()方法创建事务。";
        /// <summary>
        /// 事务和参数指定的连接不一致，请使用BeginTransaction()返回的连接作为参数。
        /// </summary>
        protected const string TRANSACTIONCONNECTIONUNMATCHEXCEPTION_MESSAGE = "事务和参数指定的连接不一致，请使用BeginTransaction()返回的连接作为参数。";
        /// <summary>
        /// 异步执行回调函数的参数为null，无法获取执行结果。
        /// </summary>
        protected const string NULL_ASYNCEXECUTE_IRESULT_MESSAGE = "异步执行回调函数的参数为null，无法获取执行结果。";
        /// <summary>
        /// <![CDATA[异步执行回调函数的State无法转换为AsyncExecuteDelegate<T>委托类型]]>
        /// </summary>
        protected const string NULL_ASYNCEXECUTE_STATE_MESSAGE = "异步执行回调函数的State无法转换为AsyncExecuteDelegate<T>委托类型。";
        #endregion

        #region 变量&属性
        private bool _ReThrowExecuteException = true;
        /// <summary>
        /// 获取或设置是否重新抛出命令执行异常，只对DbException有效。
        /// 默认为true,重新抛出命令执行异常。
        /// </summary>
        public virtual bool ReThrowExecuteException
        {
            get { return _ReThrowExecuteException; }
            set { _ReThrowExecuteException = value; }
        }

        private string _connectionString = null;
        /// <summary>
        /// 获取连接字符串
        /// </summary>
        public virtual string ConnectionString
        {
            protected set
            {
                if (_connectionString == value)
                {
                    return;
                }
                _connectionString = value;
            }
            get
            {
                return _connectionString;
            }
        }

        /// <summary>
        /// 获取当前使用的连接对象
        /// </summary>
        public virtual DbConnection Connection
        {
            get;
            protected set;
        }

        /// <summary>
        /// 获取或设置是否每次都使用同一个数据库连接对象。
        /// </summary>
        public virtual bool SingletonConnection
        {
            get;
            set;
        }

        /// <summary>
        /// 获取当前实现的数据库类型
        /// </summary>
        public abstract string DBMS
        {
            get;
        }

        /// <summary>
        /// 获取所有SQL语句块
        /// </summary>
        protected virtual TSQLStatement[] StatementArray
        {
            set;
            get;
        }

        #endregion

        #region 公共函数

        /// <summary>
        /// 构建数据库类型对应的参数
        /// </summary>
        /// <param name="paramName">参数名称</param>
        /// <param name="type">参数类型</param>
        /// <param name="direction">参数方向</param>
        /// <param name="size">参数大小</param>
        /// <param name="value">参数的值</param>
        /// <returns>DbParameter的一个实例</returns>
        public virtual DbParameter CreateParameter(string paramName, DbType type, ParameterDirection direction, int size, object value)
        {
            var command = CreateCommand();
            var param = command.CreateParameter();
            param.ParameterName = paramName;
            param.DbType = type;
            param.Direction = direction;
            param.Size = size;
            param.Value = value;
            return param;
        }

        /// <summary>
        /// 构建数据库类型对应的参数
        /// </summary>
        /// <returns>DbParameter的一个实例</returns>
        public virtual DbParameter CreateParameter(string paramName, DbType type, int size, object value)
        {
            return CreateParameter(paramName, type, ParameterDirection.Input, size, value);
        }
        /// <summary>
        /// 构建数据库类型对应的参数
        /// </summary>
        /// <returns>DbParameter的一个实例</returns>
        public virtual DbParameter CreaterParameter(string paramName, DbType type, object value)
        {
            return CreateParameter(paramName, type, ParameterDirection.Input, 0, value);
        }

        /// <summary>
        /// 关闭当前数据库连接对象，并关闭单连接模式。该方法只有在SingletonConnection设置为true时调用才有意义。
        /// </summary>
        public virtual void CloseConnection()
        {
            SingletonConnection = false;
            Connection.Dispose();
        }
        #endregion

        #region 受保护的函数

        #region 抽象函数
        /// <summary>
        /// 构建数据库类型对应的DbConnection
        /// </summary>
        /// <returns>DbConnection的一个实例</returns>
        protected abstract DbConnection CreateConnection();

        /// <summary>
        /// 构建数据库类型对应的DbCommand
        /// </summary>
        /// <returns>DbCommand的一个实例</returns>
        protected abstract DbCommand CreateCommand();

        /// <summary>
        /// 构建数据库类型对应的DataAdapter
        /// </summary>
        /// <returns>DbDataAdapter的一个实例</returns>
        protected abstract DbDataAdapter CreateAdapter(DbCommand cmd);
        #endregion

        #region 虚函数

        /// <summary>
        /// 构建DbCommand的实例
        /// </summary>
        /// <param name="command">Sql命令</param>
        /// <param name="commandType">命令类型</param>
        /// <param name="connection">连接</param>
        /// <param name="commandText">SQL语句</param>
        /// <param name="commandParameters">命令参数</param>
        protected virtual void BuildCommand(DbCommand command,
            CommandType commandType,
            DbConnection connection,
            string commandText,
            IEnumerable<DbParameter> commandParameters)
        {
            BuildCommand(null, command, commandType, connection, commandText, commandParameters);
        }

        /// <summary>
        /// 构建命令
        /// </summary>
        /// <param name="trans">事务</param>
        /// <param name="command">Sql命令</param>
        /// <param name="commandType">命令类型</param>
        /// <param name="connection">连接</param>
        /// <param name="commandText">SQL语句</param>
        /// <param name="commandParameters">命令参数</param>
        protected virtual void BuildCommand(DbTransaction trans,
            DbCommand command,
            CommandType commandType,
            DbConnection connection,
            string commandText,
            IEnumerable<DbParameter> commandParameters)
        {
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            command.Connection = connection;
            command.CommandText = commandText;
            command.CommandType = commandType;
            foreach (DbParameter parameter in commandParameters)
            {
                command.Parameters.Add(parameter);
            }

            if (trans != null)
            {
                command.Transaction = trans;
            }
        }

        /// <summary>
        /// 构建命令
        /// </summary>
        /// <param name="command">IDbCommand的一个实例</param>
        /// <param name="connection">连接</param>
        /// <param name="statementName">SQL配置节名称</param>
        /// <param name="paramValueDict">参数键值字典，Key存储参数名称，Value存储参数的值</param>
        protected virtual void BuildCommand(DbCommand command,
            DbConnection connection,
            string statementName,
            Dictionary<string, object> paramValueDict)
        {
            BuildCommand(null, command, connection, statementName, paramValueDict);
        }

        /// <summary>
        /// 构建命令
        /// </summary>
        /// <param name="trans">事务</param>
        /// <param name="command">IDbCommand的一个实例</param>
        /// <param name="connection">连接</param>
        /// <param name="statementName">SQL配置节名称</param>
        /// <param name="paramValueDict">参数键值字典，Key存储参数名称，Value存储参数的值</param>
        protected virtual void BuildCommand(DbTransaction trans,
            DbCommand command,
            DbConnection connection,
            string statementName,
            Dictionary<string, object> paramValueDict)
        {
            TSQLStatement statement = StatementArray.Single(s => s.Name.Equals(statementName, StringComparison.CurrentCultureIgnoreCase));
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            command.CommandTimeout = statement.Command.Timeout;
            command.Connection = connection;
            command.CommandText = statement.Command.Value;
            command.CommandType = EnumConvertor<CommandType>(statement.Command.Type.ToString());

            BuildParameters(command, statement.Params, paramValueDict);

            if (trans != null)
            {
                command.Transaction = trans;
            }
        }

        /// <summary>
        /// 构建命令的参数
        /// </summary>
        /// <param name="command">命令</param>
        /// <param name="paramArray">参数集合</param>
        /// <param name="paramValueDict">参数的值字典</param>
        protected virtual void BuildParameters(DbCommand command, TSQLStatementParam[] paramArray, Dictionary<string, object> paramValueDict)
        {
            if ((paramValueDict.Count == 0 && paramArray != null) || (paramArray != null && paramValueDict.Count != paramArray.Length))
            {
                throw new ArgumentException(PARAM_QUANTITY_NOT_EQUAL_TEXT, "paramValueDict");
            }
            if (paramArray != null)
            {
                foreach (TSQLStatementParam param in paramArray)
                {
                    if (!paramValueDict.ContainsKey(param.Name))
                    {
                        throw new ArgumentException(MISSING_PARAM_VALUE_FORMAT, param.Name);
                    }

                    DbParameter dbParameter = command.CreateParameter();
                    dbParameter.ParameterName = param.Name;
                    dbParameter.DbType = EnumConvertor<DbType>(param.DataType.ToString());
                    dbParameter.Size = param.Length;
                    dbParameter.Direction = EnumConvertor<ParameterDirection>(param.Direction.ToString());
                    dbParameter.Value = paramValueDict[param.Name] == null ? DBNull.Value : paramValueDict[param.Name];

                    command.Parameters.Add(dbParameter);
                }
            }
        }

        /// <summary>
        /// 把键值对数组参数转换为字典类型参数
        /// </summary>
        /// <param name="paramValueArray">键值对数组参数</param>
        /// <returns>字典类型参数</returns>
        protected virtual Dictionary<string, object> ToDictionary(KeyValuePair<string, object>[] paramValueArray)
        {
            Dictionary<string, object> paramValueDict = new Dictionary<string, object>();
            foreach (KeyValuePair<string, object> paramValue in paramValueArray)
            {
                paramValueDict.Add(paramValue.Key, paramValue.Value);
            }

            return paramValueDict;
        }

        /// <summary>
        /// 把参数值数组转为对应的语句块的字段类型参数
        /// </summary>
        /// <param name="statementName">SQL语句块</param>
        /// <param name="paramValueArray">参数值数组</param>
        /// <returns>字典类型参数</returns>
        protected virtual Dictionary<string, object> ToDictionary(string statementName, object[] paramValueArray)
        {
            TSQLStatement statement = StatementArray.Single(s => s.Name.Equals(statementName, StringComparison.CurrentCultureIgnoreCase));
            TSQLStatementParam[] paramArray = statement.Params;
            if ((paramValueArray.Length == 0 && paramArray != null) || (paramArray != null && paramValueArray.Length != paramArray.Length))
            {
                throw new ArgumentException(PARAM_QUANTITY_NOT_EQUAL_TEXT, "paramValueArray");
            }

            Dictionary<string, object> paramValueDict = new Dictionary<string, object>();
            if (paramValueArray.Length != 0)
            {
                for (int index = 0; index < paramArray.Length; index++)
                {
                    var statementParam = paramArray[index];
                    paramValueDict.Add(statementParam.Name, paramValueArray[index]);
                }
            }
            return paramValueDict;
        }
        #endregion

        #endregion

        #region 私有函数
        /// <summary>
        /// Enum数据类型转换
        /// </summary>
        /// <param name="source">源字符串，区分大小写</param>
        /// <returns>目标类型</returns>
        private T EnumConvertor<T>(string source) where T : struct
        {
            T result = (T)Enum.Parse(typeof(T), source, false);
            if (!Enum.TryParse<T>(source, false, out result))
            {
                throw new Exception(string.Format(ENUM_CONVERT_ERROR_FORMAT, source, typeof(T)));
            }
            return result;
        }

        /// <summary>
        /// 关闭数据库连接对象
        /// </summary>
        /// <param name="conn">数据库连接对象</param>
        private void CloseConnection(DbConnection conn)
        {
            if (!SingletonConnection)
            {
                conn.Dispose();
            }
        }
        #endregion

        #region 释放资源
        public void Dispose()
        {
            if (Connection != null)
            {
                Connection.Dispose();
                Connection = null;
            }
        }

        ~IDataAccessor()
        {
            if (SingletonConnection)
            {
                Dispose();
            }
        }
        #endregion 释放资源
    }
}
